package com.antd.common.log.aspect;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.antd.admin.api.entity.SysLog;
import com.antd.common.auth.util.AntdSecurityUtil;
import com.antd.common.core.util.R;
import com.antd.common.log.event.SysLogEvent;
import com.antd.common.log.util.LogUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.Arrays;

/**
 * TODO
 *
 * @author 子丶沫
 * @version 1.0
 * @date 2020/11/18 17:34
 */
@Slf4j
@Component
@Aspect
public class SysLogAspect {

    private final ThreadLocal<SysLog> threadLocal = new ThreadLocal<>();

    private final ApplicationEventPublisher publisher;

    public SysLogAspect(ApplicationEventPublisher publisher){
        this.publisher=publisher;
    }
    @Pointcut("@annotation(com.antd.common.log.annotation.SystemLog)")
    public void sysLogAspect(){}

    @Before(value = "sysLogAspect()")
    public void recordLog(JoinPoint joinPoint) throws Throwable{
        SysLog sysLog = new SysLog();
        //将当前实体保存到thread
        threadLocal.set(sysLog);
        // 开始时间
        long beginTime = Instant.now().toEpochMilli();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        sysLog.setUserName(AntdSecurityUtil.getUsername());
        sysLog.setActionUrl(URLUtil.getPath(request.getRequestURI()));
        sysLog.setStartTime(LocalDateTime.now());
        sysLog.setRequestIp(ServletUtil.getClientIP(request));
        sysLog.setRequestMethod(request.getMethod());
        sysLog.setUa(request.getHeader("user-agent"));
        //访问目标方法的参数 可动态改变参数值
        Object[] args = joinPoint.getArgs();
        // 类名
        sysLog.setClassPath(joinPoint.getTarget().getClass().getName());
        //获取执行的方法名
        String name = joinPoint.getSignature().getName();
        sysLog.setActionMethod(name);
        sysLog.setFinishTime(LocalDateTime.now());
        // 参数
        sysLog.setParams(Arrays.toString(args));
        // 获取操作类型
        sysLog.setOperateType(LogUtil.getControllerMethod(joinPoint).get("methodType"));
        sysLog.setDescription(LogUtil.getControllerMethod(joinPoint).get("description"));
        long endTime = Instant.now().toEpochMilli();
        sysLog.setConsumingTime(endTime - beginTime);
    }

    @AfterReturning(returning = "ret",pointcut ="sysLogAspect()" )
    public void afterRecordLog(Object ret){
        SysLog sysLog = threadLocal.get();
        // 处理完请求，返回内容
        R r = Convert.convert(R.class, ret);
        if(ObjectUtil.isNotNull(r)){
            if (r.getCode() == 200) {
                // 正常返回
                sysLog.setType(1);
            } else {
                sysLog.setType(2);
                sysLog.setExDetail(r.getMsg());
            }
            // 发布事件
            publisher.publishEvent(new SysLogEvent(sysLog));
            //移除当前log实体
            threadLocal.remove();
        }
    }
    @AfterThrowing(pointcut = "sysLogAspect()", throwing = "e")
    public void  afterThrow(Throwable e){
        SysLog sysLog = threadLocal.get();
        // 异常
        sysLog.setType(2);
        // 异常对象
        sysLog.setExDetail(LogUtil.getStackTrace(e));
        // 异常信息
        sysLog.setExDesc(e.getMessage());
        // 发布事件
        publisher.publishEvent(new SysLogEvent(sysLog));
        //移除当前log实体
        threadLocal.remove();
    }
}
